/* EasyWay Game Engine
 * Copyright (C) 2006 Daniele Paggi.
 *  
 * Written by: 2006 Daniele Paggi<dshnt@hotmail.com>
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.easyway.objects;

import java.io.Serializable;

import org.easyway.annotations.editor.Height;
import org.easyway.annotations.editor.Info;
import org.easyway.annotations.editor.Method;
import org.easyway.annotations.editor.Width;
import org.easyway.annotations.editor.X;
import org.easyway.annotations.editor.Y;
import org.easyway.editor.commands.user.GetPoint;
import org.easyway.interfaces.sprites.IPlain2D;
import org.easyway.quad.QuadManager;
import org.easyway.system.StaticRef;

/**
 * A 2D plain that is usable with the Quad-Engine.<br>
 * The quad-Engine is an engine that speed-up your game.<br>
 * 
 * @see org.easyway.quad.QaudManager
 * 
 * @author Paggi Daniele
 * 
 */
public class Plain2D extends BaseObject implements IPlain2D, Serializable {

	private static final long serialVersionUID = -4705260369634061760L;

	/** sizes of plain */
	protected int width, height;

	/** coordinates of object in the game world */
	protected float x, y;

	/** indicates if the Object uses the quads system or not */
	public boolean quad;

	/**
	 * indicates if the object (that USE the quad system) is present or not in
	 * the quad-greed
	 */
	private boolean addedAtQuad = false;

	/**
	 * cretes a new instace of Plain2D
	 */
	public Plain2D() {
		super(true);
		quad = false;
	}

	/**
	 * creates a new instance of plain2D<br>
	 * 
	 * @param idLayer
	 *            drawing-sheet id
	 * @param autoAdd
	 *            indicates if the Plain2D will be auto-added to the game
	 *            engine's lists
	 */

	public Plain2D(boolean quad) {
		this(0, 0, 0, 0, quad);
	}

	/**
	 * creates a new instance of plain2D<br>
	 * 
	 * @param quad
	 *            indicates if the Plain2D willbe used in the Quad-Engine.
	 */
	// public Plain2D(boolean quad) {
	// this(0, 0, 0, 0, quad);
	// }
	/**
	 * creates a new instance of Plain2D
	 * 
	 * @param x
	 *            x coordiante
	 * @param y
	 *            y coordinate
	 * @param width
	 *            width of plain
	 * @param height
	 *            height of plain
	 * @param idLayer
	 *            drawing-sheet id
	 */
	public Plain2D(float x, float y, int width, int height) {
		this(true, x, y, width, height);
	}

	/**
	 * creates a new instance of Plain2D
	 * 
	 * @param autoAdd
	 *            indicates if the Plain2D will be auto-added to the game
	 *            engine's lists
	 * @param x
	 *            x coordiante
	 * @param y
	 *            y coordinate
	 * @param width
	 *            width of plain
	 * @param height
	 *            height of plain
	 * @param idLayer
	 *            drawing-sheet id
	 */
	public Plain2D(boolean autoAdd, float x, float y, int width, int height) {
		super(autoAdd);
		this.x = x;
		this.y = y;
		if (width > 0 && height > 0)
			setSize(width, height);
	}

	/**
	 * cretes a new instace of Plain2D
	 * 
	 * @param x
	 *            x coordinate
	 * @param y
	 *            y coordiante
	 * @param width
	 *            size of plain
	 * @param height
	 *            height pf plain
	 * @param quad
	 *            if 'quad' is true the plain will be added to the Quad-Manager
	 *            else it will be used normally
	 */
	public Plain2D(float x, float y, int width, int height, boolean quad) {
		super(!quad);
		this.x = x;
		this.y = y;
		addedAtQuad = false;

		if (this.quad = quad) {
			if (StaticRef.quadManager == null)
				StaticRef.quadManager = new QuadManager(
					QuadManager.DEFAULT_WIDTH, QuadManager.DEFAULT_HEIGHT);
		}
		if (width > 0 && height > 0)
			setSize(width, height);
	}

	/**
	 * @param plain2D
	 */
	public Plain2D(Plain2D obj) {
		super(obj);
		addedAtQuad = false;
		if (this.quad = obj.quad) {
			if (StaticRef.quadManager == null)
				StaticRef.quadManager = new QuadManager(
					QuadManager.DEFAULT_WIDTH, QuadManager.DEFAULT_HEIGHT);
		}
		setXY(obj.x, obj.y);
		width = obj.width;
		height = obj.height;
		//setSize(obj.width, obj.height);
	}

	/**
	 * changes the sizes of the plain
	 * 
	 * @param width
	 *            new width
	 * @param height
	 *            new height
	 */
	@Method("set the Dimension")//
	public void setSize(@Width @Info("width") int width,
			@Height @Info("height") int height) {
		if (quad) {
			// && (StaticRef.quadManager.testUpdateSize(x, y, this.width,
			// this.height, width, height) || !addedAtQuad)) {
			removeQuadsUpdate();
			this.width = width;
			this.height = height;
			addQuadsUpdate();
		} else {
			this.width = width;
			this.height = height;
		}
	}

	@Method("set the Width") //
	public void setWidth(@Width int width) {
		setSize(width, height);
	}

	@Method("set the Height") //
	public void setHeight(@Height int height) {
		setSize(width, height);
	}

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	/**
	 * returns the x coordinate
	 * 
	 */
	public float getX() {
		return x;
	}

	/**
	 * returns the y coordinate
	 * 
	 */
	public float getY() {
		return y;
	}

	public float getXOnScreen() {
		return x;
	}

	public float getYOnScreen() {
		return y;
	}

	/**
	 * sets a new x position
	 * 
	 */
	public void setX(float x) {
		if (quad && StaticRef.quadManager.testUpdateX(x, this.x)) {
			removeQuadsUpdate();
			this.x = x;
			addQuadsUpdate();
		} else
			this.x = x;
	}

	/**
	 * sets a new y position *
	 * 
	 */
	public void setY(float y) {
		if (quad && StaticRef.quadManager.testUpdateY(y, this.y)) {
			removeQuadsUpdate();
			this.y = y;
			addQuadsUpdate();
		} else
			this.y = y;
	}

	/**
	 * sets a new x,y position
	 * 
	 * @param x
	 *            new x position
	 * @param y
	 *            new y position
	 */
	@Method("move to")//
	public void setXY(@X @Info("x") float x, @Y @Info("y") float y) {
		if (quad && StaticRef.quadManager.testUpdate(x, y, this.x, this.y)) {
			removeQuadsUpdate();
			this.x = x;
			this.y = y;
			addQuadsUpdate();
		} else {
			this.x = x;
			this.y = y;
		}
	}

	/**
	 * increments it's coordinates of incx, incy x += incx;<br>
	 * y += incy;<br>
	 * 
	 * @param incx
	 * @param incy
	 */
	@Method("move of")//
	public void move(@Info("inc x") float incx, @Info("inc y") float incy) {
		if (quad
				&& StaticRef.quadManager.testUpdate(x + incx, y + incy, this.x,
					this.y)) {
			removeQuadsUpdate();
			x += incx;
			y += incy;
			addQuadsUpdate();
		} else {
			x += incx;
			y += incy;
		}
	}

	/**
	 * decrements it's x coordiante of incx<br>
	 * x--;
	 */
	public void decX() {
		if (quad && StaticRef.quadManager.testUpdateX(this.x - 1, this.x)) {
			removeQuadsUpdate();
			--x;
			addQuadsUpdate();
		} else
			--x;
	}

	/**
	 * decrements it's y coordiante of incy<br>
	 * y--;
	 */
	public void decY() {
		if (quad && StaticRef.quadManager.testUpdateY(this.y - 1, this.y)) {
			removeQuadsUpdate();
			--y;
			addQuadsUpdate();
		} else
			--y;
	}

	/**
	 * increments it's x coordiante of 1
	 */
	public void incX() {
		if (quad && StaticRef.quadManager.testUpdateX(this.x + 1, this.x)) {
			removeQuadsUpdate();
			++x;
			addQuadsUpdate();
		} else
			++x;
	}

	/**
	 * increments it's y coordiante of 1
	 */
	public void incY() {
		if (quad && StaticRef.quadManager.testUpdateY(this.y + 1, this.y)) {
			removeQuadsUpdate();
			++y;
			addQuadsUpdate();
		} else
			++y;
	}

	/**
	 * increments it's x coordinate of incx<br>
	 * x += incx;
	 */
	public void incX(float incx) {
		if (quad && StaticRef.quadManager.testUpdateX(this.x + incx, this.x)) {
			removeQuadsUpdate();
			x += incx;
			addQuadsUpdate();
		} else
			x += incx;
	}

	/**
	 * increments it's y coordiante of incy<br>
	 * y += incy;
	 */
	public void incY(float incy) {
		if (quad && StaticRef.quadManager.testUpdateY(this.y + incy, this.y)) {
			removeQuadsUpdate();
			y += incy;
			addQuadsUpdate();
		} else
			y += incy;
	}

	/**
	 * removes the plain from the Quad-Engine<br>
	 */
	private void removeQuadsUpdate() {
		if (!addedAtQuad)
			return;
		if (isLoopable)
			StaticRef.quadManager.removeLoop(this);
		if (isDrawable)
			StaticRef.quadManager.removeRender(this);
	}

	/**
	 * adds the plain from the Quad-Engine
	 * 
	 */
	private void addQuadsUpdate() {
		if (isLoopable)
			StaticRef.quadManager.addLoop(this);
		if (isDrawable)
			StaticRef.quadManager.addRender(this);
		addedAtQuad = true;
	}

	/**
	 * indicates if add the object to the quad manager or not
	 * 
	 * @param value
	 */
	@Method("use Quad system")//
	public void useQuad(@Info("use quad?") boolean value) {
		if ((quad = value) == false) {
			removeQuadsUpdate();
		} else if (StaticRef.quadManager == null)
			StaticRef.quadManager = new QuadManager(QuadManager.DEFAULT_WIDTH,
				QuadManager.DEFAULT_HEIGHT);
	}

	public void destroy() {
		if (isDestroyed())
			return;
		if (quad)
			removeQuadsUpdate();
		super.destroy();
	}

	@Method("Dupe")//
	public void dupe() {
//		 try {
		final Plain2D thisObj = this;
		new Thread() {
			public void run() {
				for (;;) {
					float xy[] = GetPoint.getPoint();
					if (xy == null) {
						break;
					}
					Plain2D obj = (Plain2D) thisObj.clone();
					obj.setXY(xy[2], xy[3]);
				}
			}
		}.start();
		// } catch (CloneNotSupportedException e) {
		// e.printStackTrace();
		//		
		//		 }

	}

	public Object clone() {
		return new Plain2D(this);
	}

}
